/***************************************************************
*  Copyright (c) 2007 by GroupMe! Team (www.groupme.net)
*  All rights reserved.
*
*  This file is part of the GroupMe! Project. Source code of 
*  this project is closed and redistribution of this code is
*  prohibited. 
*  
*  Contact: http://www.groupme.net
*
*  This copyright notice MUST APPEAR in all copies of the file!
***************************************************************/
package net.groupme.user;

import java.sql.Types;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.object.SqlUpdate;

import net.groupme.controller.model.HtmlFormattedElement;
import net.groupme.exceptions.DeleteFailedException;
import net.groupme.exceptions.DuplicateUsernameException;
import net.groupme.exceptions.InsertFailedException;
import net.groupme.exceptions.MailException;
import net.groupme.exceptions.UpdateFailedException;
import net.groupme.exceptions.UserNotExistingException;
import net.groupme.gui.constants.VIEW_VOCABULARY;
import net.groupme.mail.MailSenderUtility;
import net.groupme.model.DashboardAnnouncement;
import net.groupme.model.Group;
import net.groupme.model.GroupVisualization;
import net.groupme.model.MODEL_CONSTANTS;
import net.groupme.model.ObjectFactory;
import net.groupme.model.Resource;
import net.groupme.model.ResourceVisualization;
import net.groupme.model.User;
import net.groupme.model.cache.CachedObject;
import net.groupme.model.cache.CachedObjectType;
import net.groupme.model.dao.DATABASE_CONSTANTS;
import net.groupme.model.dao.DBProperties;
import net.groupme.utils.sql.GetDashboardAnnouncementsQuery;
import net.groupme.utils.sql.GetGroupsQuery;
import net.groupme.utils.sql.GetStringFromTableWhereIDQuery;
import net.groupme.utils.sql.GetStringQuery;
import net.groupme.utils.sql.GroupQueryingUtility;
import net.groupme.utils.sql.UserQueryUtility;

/**
 * created on Jun 20, 2007
 *  
 * This class provides some functionality to manage the user. In general
 * it encapsulates just a {@link User} instance.
 * 
 * @author  Fabian Abel, <a href="mailto:abel@l3s.de">abel@l3s.de</a>
 * @author last edited by $Author: fabian $
 * 
 * @version $Revision: 1.22 $ $Date: 2009-04-22 18:28:54 $
 */
public class UserManager {
	// table
	private static final String TABLE_USERGROUPVISUALIZATION = DBProperties
			.getString("GroupmeDB.TABLE_USERGROUPVISUALIZATION"); //$NON-NLS-1$

	// columns
	private static final String USERGROUPVISUALIZATION_VISUALIZATION_ID = DBProperties
			.getString("GroupmeDB.USERGROUPVISUALIZATION_VISUALIZATION_ID"); //$NON-NLS-1$

	private static final String USERGROUPVISUALIZATION_USER_ID = DBProperties
			.getString("GroupmeDB.USERGROUPVISUALIZATION_USER_ID"); //$NON-NLS-1$

	private static final String USERGROUPVISUALIZATION_GROUP_ID = DBProperties
			.getString("GroupmeDB.USERGROUPVISUALIZATION_GROUP_ID"); //$NON-NLS-1$

	private static final String USERGROUPVISUALIZATION_ROLE = DBProperties
			.getString("GroupmeDB.USERGROUPVISUALIZATION_ROLE"); //$NON-NLS-1$
	
	/** the maximum number of groups that should be listed as <i>latests groups</i> */
	private static final int MAX_NUMBER_OF_LATEST_GROUPS = 3;
	
	/** the maximum number of resources that should be displayed from the user's resource queue */
	private static final int MAX_NUMBER_OF_RESOURCES_OF_QUEUE = MAX_NUMBER_OF_LATEST_GROUPS;

	/** the maximum number of announcements that should be displayed to the user */
	private static final int MAX_NUMBER_OF_SHOWN_ANNOUNCEMENTS = 20;
	
	/** the number of announcements that should be displayed to the user */
	private static final int NUMBER_OF_SHOWN_ANNOUNCEMENTS = 5;
	
	/** the minimum number of announcements that should be displayed to the user */
	private static final int MIN_NUMBER_OF_SHOWN_ANNOUNCEMENTS = 3;

	/** Logger for this class and subclasses */
	protected final Log logger = LogFactory.getLog(getClass());
	
	/** the user which is managed by this class */
	private User user = null;
	
	/** the sessionId of the user */
	private String sessionId = null;
	
	/** cached overall tag cloud */
	private CachedObject cachedUserTagCloud = null;

	/**
	 * Creates an empty {@link UserManager} instance.
	 */
	public UserManager(){
		super();
	}
	
	/**
	 * Sets the user to manage
	 * @param user the user to manage
	 */
	public void setUser(User user){
		this.user = user;
		cachedUserTagCloud = ObjectFactory.getCachedObject(CachedObjectType.TAG_CLOUD_USER, user, null);
	}
	
	/**
	 * Returns the managed user
	 * @return the managed user or <code>null</code> if the user is not logged in
	 */
	public User getUser(){
		return user;
	}


	/**
	 * This method returns the sessionId of a UserManager object.
	 * @return the sessionId
	 */
	public String getSessionId() {
		return sessionId;
	}

	/**
	 * This method sets the sessionId of a UserManager object. But only
	 * if the sessionId was not set before...
	 * 
	 * @param sessionId the sessionId to set
	 */
	public void setSessionId(String sessionId) {
		if(this.sessionId == null){
			this.sessionId = sessionId;
		}
	}

	/**
	 * Returns the <i>login status</i> of the actual user.
	 * @return <code>true</code> if the user is logged in, otherwise <code>false</code>
	 */
	public boolean isLoggedIn(){
		return user != null;
	}
	
	/**
	 * Returns the <i>subscriber status</i> of the actual user.
	 * @return <code>true</code> if the user has subscribed any groups, otherwise <code>false</code>
	 */
	public boolean isSubscriber(){
		return !this.getLatestSubscribedGroupsOfUser().isEmpty();
	}
	
	/** Checks if current user is allowed to edit the given GroupVisualization and given Group.
	 * 
	 * @param groupvisualization
	 * @return true if allowed to
	 */
	public boolean isAllowedToEdit(Group group, GroupVisualization groupvisualization){
		String query = "SELECT " + USERGROUPVISUALIZATION_ROLE + " FROM " + TABLE_USERGROUPVISUALIZATION 
					 + " WHERE " + USERGROUPVISUALIZATION_USER_ID + "=" + this.getUser().getId()
					 + " AND " + USERGROUPVISUALIZATION_GROUP_ID + "=" + group.getId()
					 + " AND " + USERGROUPVISUALIZATION_VISUALIZATION_ID + "=" + groupvisualization.getId();
		GetStringQuery getRoleQuery = new GetStringQuery(
				ObjectFactory.ds, query, USERGROUPVISUALIZATION_ROLE);
		String role = (String) getRoleQuery.execute().iterator().next();
		if (role.equals(DATABASE_CONSTANTS.User_ROLE_OWNER)){
			return true;
		}
		return false;
	}
	
	/**
	 * If username and password are correct: Logs in a users and creates a {@link User} instance that will be wrapped by this
	 * {@link UserManager} object. Otherwise: <code>false</code> is returned.
	 * @param username the username
	 * @param password the corresponding password
	 * @return a {@link UserManagerMessages} indicating if login was successful or not
	 */
	public UserManagerMessages login(String username, String password){
		User user = null;
		try {
			user = ObjectFactory.getUser(username);
	    	boolean loginSuccessful = user.checkPassword(password);
	    	if(loginSuccessful){	    		
	    		this.setUser(user);
	    		
	    		logger.info("User has logged in: " + this.user.getUserName());
	    		
	    		return UserManagerMessages.LOGIN_SUCCESSFUL;
	    	}else{
	    		return UserManagerMessages.PASSWORD_INCORRECT;
	    	}
		} catch (UserNotExistingException e) {
			return UserManagerMessages.USERNAME_DOES_NOT_EXISTS;
		}
	}
	
	/**
	 * Sets the user to manage to <code>null</code>
	 */
	public void logout(){
		this.user = null;
		this.cachedUserTagCloud = null;
	}
	
	/**
	 * Method tries to register a new user.
	 * @param username the username
	 * @param password the user's password
	 * @param email email address
	 * @param firstName first name
	 * @param lastName last name
	 * @param foafDocumentURL URL of the user's FOAF document
	 * @param photo a photo of the user
	 * @return one of {@link UserManagerMessages} that indicates wheather registration was successful or not
	 */
	public UserManagerMessages register(String username, String password, String email, String firstName, String lastName, String foafDocumentURL, String photo){
		User user = null;
		try {
			//TODO: Metadata Stuff for Users and Resources (e.g. to store the url of the user's FOAF document  
			user = ObjectFactory.createUser(username);
			user.setPassword(password);
			user.setFirstName(firstName);
			user.setLastName(lastName);
			user.setEmail(email);
			if (!"".equals(foafDocumentURL)){
				user.setFoaf(foafDocumentURL);
			}
			if (!"".equals(photo)){
				user.setPhoto(photo);
			}
			//Sending an email that says that the registration was successful.
			try {
				MailSenderUtility.sendWelcomeMail(user, password);
			} catch (MailException e) {
				// keine Exception schmeissen!
				logger.error("Failed to send a welcome email!", e);
			}
			return UserManagerMessages.REGISTRATION_SUCCESSFUL;
		} catch (InsertFailedException e) {
			e.printStackTrace();
			return UserManagerMessages.UPDATE_OR_INSERT_FAILED;
		} catch (UpdateFailedException e) {
			e.printStackTrace();
			return UserManagerMessages.UPDATE_OR_INSERT_FAILED;
		} catch (DuplicateUsernameException e) {
			return UserManagerMessages.USERNAME_ALLREADY_EXISTS;
		} 
	}
	
	/**
	 * Method tries to update the user details.
	 * 
	 * @param firstName first name
	 * @param lastName last name
	 * @param email email address
	 * @param foafDocumentURL URL of the user's FOAF document
	 * @param photo a photo of the user
	 * @return one of {@link UserManagerMessages} that indicates wheather update was successful or not
	 */
	public UserManagerMessages updateDetails(String firstName, String lastName, String email, String foafDocumentURL, String photo){
		try {
			//TODO: Metadata Stuff for Users and Resources (e.g. to store the url of the user's FOAF document
			user.setFirstName(firstName);
			user.setLastName(lastName);
			user.setEmail(email);
			if(!foafDocumentURL.equals("")){
			user.setFoaf(foafDocumentURL);
			}
			user.setPhoto(photo);
			return UserManagerMessages.UPDATE_SUCCESSFUL;
		} catch (UpdateFailedException e) {
			e.printStackTrace();
			return UserManagerMessages.UPDATE_OR_INSERT_FAILED;
		} 
	}
	
	/**
	 * Method tries to update the user's password.
	 * 
	 * @param password the new password
	 */
	public UserManagerMessages updatePassword(String password){
		try {
			user.setPassword(password);
			return UserManagerMessages.UPDATE_SUCCESSFUL;
		} catch (UpdateFailedException e) {
			e.printStackTrace();
			return UserManagerMessages.UPDATE_OR_INSERT_FAILED;
		} 
	}

	/**
	 * This method returns the cachedOverallTagCloud of a SearchManager object.
	 * @return the cached user tag cloud or <code>null</code> if no tag cloud is cached for the user
	 */
	public HtmlFormattedElement getCachedTagCloud() {
		if(this.cachedUserTagCloud == null){
			this.cachedUserTagCloud = ObjectFactory.getCachedObject(CachedObjectType.TAG_CLOUD_USER, user, null);
		}
		if(this.cachedUserTagCloud == null){
			return null;
		}
		return (HtmlFormattedElement) this.cachedUserTagCloud.getChachedObject();
	}

	/**
	 * This method sets the cachedOverallTagCloud of a SearchManager object.
	 * @param cachedTagCloud the user tag cloud to cache
	 */
	public void setCachedTagCloud(HtmlFormattedElement cachedTagCloud) {
		if(this.cachedUserTagCloud == null){
			this.cachedUserTagCloud = ObjectFactory.createCachedObject(cachedTagCloud, this.user, CachedObjectType.TAG_CLOUD_USER);
		}else{
			try {
				this.cachedUserTagCloud.setChachedObject(cachedTagCloud);
			} catch (UpdateFailedException e) {
				e.printStackTrace();
				logger.error("Failed to cache the tag cloud.", e);
			}
		}
	}
	
	/**
	 * This method adds the group with the given Id to the subscribed groups of the User.
	 * @param groupId the id of the group which should be subscribed
	 * @throws InsertFailedException 
	 */
	public void subscribeGroup(int groupId) throws InsertFailedException{
		SqlUpdate su = new SqlUpdate(
				ObjectFactory.ds,
				"INSERT INTO " + DATABASE_CONSTANTS.TABLE_USERGROUP//$NON-NLS-1$
						+ " (" + DATABASE_CONSTANTS.USER_GROUP_USER_ID+ ", " + DATABASE_CONSTANTS.USER_GROUP_GROUP_ID + ", " + DATABASE_CONSTANTS.USER_GROUP_USERGROUP_ROLE + ") VALUES (?, ?, ?)"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ 
		su.declareParameter(new SqlParameter("uID", Types.INTEGER)); //$NON-NLS-1$
		su.declareParameter(new SqlParameter("gID", Types.INTEGER)); //$NON-NLS-1$
		su.declareParameter(new SqlParameter("role", Types.VARCHAR)); //$NON-NLS-1$
		su.compile();
		try {
			Object[] oa = new Object[3];
			oa[0] = new Integer(this.user.getId());
			oa[1] = new Integer(groupId);
			oa[2] = new String("subscriber");
			int count = su.update(oa);
			if (count == 0)
				throw new InsertFailedException();
		} catch (NumberFormatException e) {
			logger.debug(e.getMessage());
		}
		SqlUpdate su2 = new SqlUpdate(ObjectFactory.ds,
				"Insert Into " + DATABASE_CONSTANTS.TABLE_LOG_EVENT_ACTION 
				+ " (" + DATABASE_CONSTANTS.TABLE_LOG_EVENT_ACTION_USER_ID + ", "
			    + DATABASE_CONSTANTS.TABLE_LOG_EVENT_ACTION_GROUP_ID + ", "
				+ DATABASE_CONSTANTS.TABLE_LOG_EVENT_ACTION_RESOURCE_ID + ", "
				+ DATABASE_CONSTANTS.TABLE_LOG_EVENT_ACTION_TAG_ID + ", "
				+ DATABASE_CONSTANTS.TABLE_LOG_EVENT_ACTION_ACTION  
				 + ") VALUES (?, ?, ?, ?, ?)");
		su2.declareParameter(new SqlParameter("UserID", Types.INTEGER)); 
		su2.declareParameter(new SqlParameter("GroupID", Types.INTEGER));
		su2.declareParameter(new SqlParameter("ResourceID", Types.INTEGER)); 
		su2.declareParameter(new SqlParameter("TagID", Types.INTEGER)); 		
		su2.declareParameter(new SqlParameter("Action", Types.VARCHAR)); 	
		Object[] oa2 = new Object[5];
		oa2[0] = this.user.getId();	
		oa2[1] = groupId;
		oa2[2] = groupId;
		oa2[3] = null;
		oa2[4] = DATABASE_CONSTANTS.TABLE_LOG_EVENT_ACTION_GROUP_SUBSCRIBED;
		int count2 = su2.update(oa2);
		if (count2 == 0){
			throw new InsertFailedException();
		}
	}
	
	/**
	 * This method remuves the group with the given Id from the subscribed groups of the User.
	 * @param groupId the id of the group which should be unsubscribed
	 * @throws eleteFailedException 
	 */
	
	public void unsubscribeGroup(int groupID) throws DeleteFailedException {
		SqlUpdate su = new SqlUpdate(ObjectFactory.ds,
				"DELETE FROM " + DATABASE_CONSTANTS.TABLE_USERGROUP + " WHERE " //$NON-NLS-1$ //$NON-NLS-2$
						+ DATABASE_CONSTANTS.USER_GROUP_USER_ID
						+ " = ? AND " + DATABASE_CONSTANTS.USER_GROUP_GROUP_ID + " = ?"); //$NON-NLS-1$ //$NON-NLS-2$
		su.declareParameter(new SqlParameter("uID", Types.INTEGER)); //$NON-NLS-1$
		su.declareParameter(new SqlParameter("gID", Types.INTEGER)); //$NON-NLS-1$
		su.compile();
		try {
			Object[] oa = new Object[2];
			oa[0] = new Integer(this.user.getId());
			oa[1] = new Integer(groupID);
			int count = su.update(oa);
			if (count == 0)
				throw new DeleteFailedException();
		} catch (NumberFormatException e) {
			logger.debug(e.getMessage());
		}
		SqlUpdate su2 = new SqlUpdate(ObjectFactory.ds,
				"Insert Into " + DATABASE_CONSTANTS.TABLE_LOG_EVENT_ACTION 
				+ " (" + DATABASE_CONSTANTS.TABLE_LOG_EVENT_ACTION_USER_ID + ", "
			    + DATABASE_CONSTANTS.TABLE_LOG_EVENT_ACTION_GROUP_ID + ", "
				+ DATABASE_CONSTANTS.TABLE_LOG_EVENT_ACTION_RESOURCE_ID + ", "
				+ DATABASE_CONSTANTS.TABLE_LOG_EVENT_ACTION_TAG_ID + ", "
				+ DATABASE_CONSTANTS.TABLE_LOG_EVENT_ACTION_ACTION  
				 + ") VALUES (?, ?, ?, ?, ?)");
		su2.declareParameter(new SqlParameter("UserID", Types.INTEGER)); 
		su2.declareParameter(new SqlParameter("GroupID", Types.INTEGER));
		su2.declareParameter(new SqlParameter("ResourceID", Types.INTEGER)); 
		su2.declareParameter(new SqlParameter("TagID", Types.INTEGER)); 		
		su2.declareParameter(new SqlParameter("Action", Types.VARCHAR)); 	
		Object[] oa2 = new Object[5];
		oa2[0] = this.user.getId();	
		oa2[1] = groupID;
		oa2[2] = groupID;
		oa2[3] = null;
		oa2[4] = DATABASE_CONSTANTS.TABLE_LOG_EVENT_ACTION_GROUP_UNSUBSCRIBED;
		su2.update(oa2);
	}
	
	/**
	 * This method returns the top k relevant actions from the action-loggin table for this user.
	 * @param topK the max number of results
	 * @return a List of DashboardAnnouncements
	 */
	public List<DashboardAnnouncement> getDashboardAnnouncements(int topK){
		String query = "SELECT lea." + DATABASE_CONSTANTS.TABLE_LOG_EVENT_ACTION_USER_ID
			+ ",lea." + DATABASE_CONSTANTS.TABLE_LOG_EVENT_ACTION_GROUP_ID
			+ ",lea." + DATABASE_CONSTANTS.TABLE_LOG_EVENT_ACTION_RESOURCE_ID
			+ ",lea." + DATABASE_CONSTANTS.TABLE_LOG_EVENT_ACTION_TAG_ID
			+ ",lea." + DATABASE_CONSTANTS.TABLE_LOG_EVENT_ACTION_ACTION
			+ ",lea." + DATABASE_CONSTANTS.TABLE_LOG_EVENT_ACTION_TIME_AND_DATE
			+ ", "    + DATABASE_CONSTANTS.TABLE_USERGROUP + "." + DATABASE_CONSTANTS.USER_GROUP_USERGROUP_ROLE
			+ ",g." + DATABASE_CONSTANTS.RESOURCE_NAME + " AS g" + DATABASE_CONSTANTS.RESOURCE_NAME
			+ ", "    + DATABASE_CONSTANTS.TABLE_RESOURCE + "." + DATABASE_CONSTANTS.RESOURCE_NAME
			+ ", "    + DATABASE_CONSTANTS.TABLE_TAG + "." + DATABASE_CONSTANTS.TAG_KEYWORD
			+ " FROM "+ DATABASE_CONSTANTS.TABLE_LOG_EVENT_ACTION + " AS lea "
			+ "JOIN " + DATABASE_CONSTANTS.TABLE_USERGROUP 
			+ " ON "  + DATABASE_CONSTANTS.TABLE_USERGROUP + "." + DATABASE_CONSTANTS.USER_GROUP_GROUP_ID 
			+ " = "   + "lea." + DATABASE_CONSTANTS.TABLE_LOG_EVENT_ACTION_GROUP_ID
			+ " JOIN " + DATABASE_CONSTANTS.TABLE_RESOURCE + " AS g" 
			+ " ON g." + DATABASE_CONSTANTS.RESOURCE_ID
			+ " = "   + "lea." + DATABASE_CONSTANTS.TABLE_LOG_EVENT_ACTION_GROUP_ID
			+ " JOIN " + DATABASE_CONSTANTS.TABLE_RESOURCE 
			+ " ON "  + DATABASE_CONSTANTS.TABLE_RESOURCE + "." + DATABASE_CONSTANTS.RESOURCE_ID
			+ " = "   + "lea." + DATABASE_CONSTANTS.TABLE_LOG_EVENT_ACTION_RESOURCE_ID
			+ " LEFT JOIN " + DATABASE_CONSTANTS.TABLE_TAG
			+ " ON "  + DATABASE_CONSTANTS.TABLE_TAG + "." + DATABASE_CONSTANTS.TAG_ID
			+ " = "   + "lea." + DATABASE_CONSTANTS.TABLE_LOG_EVENT_ACTION_TAG_ID
			+ " WHERE (" + DATABASE_CONSTANTS.TABLE_USERGROUP + "." + DATABASE_CONSTANTS.USER_GROUP_USER_ID 
			+ "=" + this.user.getId() + " AND lea." + DATABASE_CONSTANTS.TABLE_LOG_EVENT_ACTION_USER_ID + " != " + this.user.getId()+ ") "
			+ "ORDER BY lea." + DATABASE_CONSTANTS.TABLE_LOG_EVENT_ACTION_TIME_AND_DATE 
			+ " desc limit " + topK;
		logger.info(query);
		GetDashboardAnnouncementsQuery getAnnouncements = new GetDashboardAnnouncementsQuery(ObjectFactory.ds, query);
		Iterator iter = getAnnouncements.execute().iterator();
		if (iter.hasNext()) {
			return (List<DashboardAnnouncement>) getAnnouncements.execute().iterator().next();
		}
		logger.info("0 Dashboard announcements found.");
		return new ArrayList<DashboardAnnouncement>();
	}
	
	/**
	 * This method returns the latest actions from the action-loggin table for this user.
	 * @return a List of DashboardAnnouncements
	 */
	public List<DashboardAnnouncement> getLatest5DashboardAnnouncements(){
		return this.getDashboardAnnouncements(NUMBER_OF_SHOWN_ANNOUNCEMENTS);
	}
	
	/**
	 * This method returns the latest 3 actions from the action-loggin table for this user.
	 * @return a List of DashboardAnnouncements
	 */
	public List<DashboardAnnouncement> getLatest3DashboardAnnouncements(){
		return this.getDashboardAnnouncements(MIN_NUMBER_OF_SHOWN_ANNOUNCEMENTS);
	}
	
	/**
	 * This method returns the latest actions from the action-loggin table for this user.
	 * @return a List of DashboardAnnouncements
	 */
	public List<DashboardAnnouncement> getLatest20DashboardAnnouncements(){
		return this.getDashboardAnnouncements(MAX_NUMBER_OF_SHOWN_ANNOUNCEMENTS);
	}
	
	public static String[] ownerOrEditor = {MODEL_CONSTANTS.ROLE_OWNER, MODEL_CONSTANTS.ROLE_EDITOR};
	public static String[] subscriber = {MODEL_CONSTANTS.ROLE_SUBSCRIBER};
	
	/**
	 * This method returns those groups of the user that have been modified at last.
	 * Here, by modifying we mean: tagging of group or resources within group. This 
	 * method returns an ordered list. The first element is the group is the newest modified group. 
	 * @param topK the top K (K newest groups) - number of groups that should be contained in the ranking
	 * @return ranking of most actual groups
	 */
	public List<Group> getLatestCreatedGroupsOfUserWithMaxNumber(int topK){
		return GroupQueryingUtility.getLatestGroupsOfUser(this.user, ownerOrEditor, topK);
	}
	
	/**
	 * This method returns the groups the user has subscribed at last.
	 * This method returns an ordered list. The first element is the group is the newest modified group. 
	 * @param topK the top K (K newest groups) - number of groups that shold be contained in the ranking
	 * @return ranking of subscribed groups
	 */
	public List<Group> getLatestSubscribedGroupsOfUserWithMaxNumber(int topK){
		return GroupQueryingUtility.getLatestGroupsOfUser(this.user, subscriber, topK);
	}
	
	/**
	 * This method returns the five groups the user has subscribed at last.
	 * This method returns an ordered list. The first element is the group is the newest modified group. 
	 * @return ranking of subscribed groups
	 */
	public List<Group> getLatestSubscribedGroupsOfUser(){
		return GroupQueryingUtility.getLatestGroupsOfUser(this.user, subscriber, MAX_NUMBER_OF_LATEST_GROUPS);
	}
	
	/**
	 * This method returns the all groups the user has subscribed.
	 * This method returns an ordered list ordered by the given column. 
	 * @param orderedBy the column by which the list should be ordered
	 * @return ranking of subscribed groups
	 */
	public List<Group> getSubscribedGroupsOfUserOrderedBy(String orderedBy){
		return GroupQueryingUtility.getAllGroupsOfUserOrdered(this.user, subscriber, orderedBy);
	}
	
	/**
	 * This method returns those groups of the user that have been modified at last.
	 * Here, by modifying we mean: tagging of group or resources within group. This 
	 * method returns an ordered list. The first element is the group is the newest modified group. 
	 * @return ranking of most actual groups
	 */
	public List<Group> getLatestCreatedGroupsOfUser(){
		logger.info("latest groups are gathered...");
		
		List<Group> groups = GroupQueryingUtility.getLatestGroupsOfUser(this.user, ownerOrEditor, MAX_NUMBER_OF_LATEST_GROUPS);
		for (Group group : groups) {
			logger.info(group.getName());
		}
		return groups;
	}
	
	/**
	 * This method returns those groups of the user that have been modified at last.
	 * Here, by modifying we mean: tagging of group or resources within group. This 
	 * method returns an ordered list. The first element is the group is the newest modified group. 
	 * @return ranking of most actual groups
	 */
	public List<Group> getAllGroupsOfUser(){
		logger.info("groups are gathered for User: " + this.user.getId());
		List<Group> groups = GroupQueryingUtility.getAllGroupsOfUser(this.user, ownerOrEditor);
		for (Group group : groups) {
			logger.info(group.getName());
		}
		return groups;
	}
	
	/**
	 * This method returns all groups of the user ordered by the given column.
	 * @param orderedBy the column by which the list should be ordered
	 * @return ranking of most actual groups
	 */
	public List<Group> getAllGroupsOfUserOrderedBy(String orderedBy){
		logger.info("groups are gathered for User: " + this.user.getId());
		List<Group> groups = GroupQueryingUtility.getAllGroupsOfUserOrdered(this.user, ownerOrEditor, orderedBy);
		for (Group group : groups) {
			logger.info(group.getName());
		}
		return groups;
	}
	
	/**
	 * This method returns the special <i>queue group</i> of the actual user.
	 * @return the special <i>queue group</i> which contains resources that are not already assigned to a group.
	 */
	public Group getQueueOfUser(){
		return UserQueryUtility.getResourceQueueOfUser(this.user.getId());
	}
	
	/**
	 * This method returns the x latest resources of the user's resource queue (x is {@link UserManager#MAX_NUMBER_OF_RESOURCES_OF_QUEUE}).
	 * @return the x latest resources of the user's resource queue (x is {@link UserManager#MAX_NUMBER_OF_RESOURCES_OF_QUEUE}).
	 */
	public List<Resource> getLatestResourcesOfQueue(){
		return UserQueryUtility.getLatestResourcesWithinQueueOfUser(this.user, MAX_NUMBER_OF_RESOURCES_OF_QUEUE);
	}
	
	/**
	 * This method returns the special <i>queue group</i> of the actual user.
	 * @return the special <i>queue group</i> which contains resources that are not already assigned to a group.
	 */
	public List<Resource> getResourcesOfQueue(){
		return UserQueryUtility.getLatestResourcesWithinQueueOfUser(this.user, 10000000);
	}
	
	/**
	 * This method checks if the user's resource queue is empty 
	 * @return <code>true</code> if the user's resource queue is empty or does not exist, otherwise <code>false</code>
	 */
	public boolean isQueueOfUserEmpty(){
		Group queue = UserQueryUtility.getResourceQueueOfUser(this.user.getId());
		if(queue == null || queue.getResources().isEmpty()){
			return true;
		}
		return false;
	}

	
	public List<String> getTest(){
		List<String> test = new ArrayList<String>();
		test.add("a");
		test.add("g");
		test.add("b");
		return test;
	}
}
